package com.ex

import com.android.build.api.transform.*
import com.android.build.gradle.internal.pipeline.TransformManager
import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
import org.gradle.api.Project
import org.objectweb.asm.*
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStreamWriter
import java.util.jar.JarFile

class ThinRTransform constructor(val project: Project) : Transform() {

    private val transformName = "thinRPlugin"

    private val pathFile = "pathFile.txt"


    private val Rmap = hashMapOf<String, Int>()

    override fun getName(): String {
        return transformName
    }

    override fun getInputTypes(): MutableSet<QualifiedContent.ContentType> {
        return TransformManager.CONTENT_CLASS
    }

    override fun isIncremental() = false

    override fun getScopes(): MutableSet<in QualifiedContent.Scope> =
        TransformManager.SCOPE_FULL_PROJECT

    override fun transform(transformInvocation: TransformInvocation?) {
        super.transform(transformInvocation)

        var count = 0

        count++

        val outputProvider = transformInvocation!!.outputProvider
        val pathFile = File(project.buildDir, "cjj/pathFile.txt")

        if (pathFile.parentFile.exists().not())
            pathFile.parentFile.mkdirs()
        val pathFileWriter = OutputStreamWriter(FileOutputStream(pathFile))




        transformInvocation.inputs.forEach {

            it.directoryInputs.forEach { dirInput ->
                dirInput.file.listFiles().forEach { file ->

                    pathFileWriter.write(file.absolutePath + "\n")
                    pathFileWriter.flush()

                    if (isRClass(file.absolutePath)) {
                        pathFileWriter.write("$transformName:" + file.absolutePath + "\n")
                        pathFileWriter.flush()
                        count++
                    }
                }

                val dest = outputProvider.getContentLocation(
                    dirInput.name, dirInput.contentTypes, dirInput.scopes,
                    Format.DIRECTORY
                )
                FileUtils.copyDirectory(dirInput.file, dest)
            }


            it.jarInputs.forEach {

                it.file.absolutePath.contains("R.class")

                val jarFile = JarFile(it.file)

                val entries = jarFile.entries()

                while (entries.hasMoreElements()) {

                    val jarEntry = entries.nextElement()

                    if (isRClass(jarEntry.name)) {
                        pathFileWriter.write("$transformName:" + jarEntry.name + "\n")
                        pathFileWriter.flush()

                        collectRField(jarFile.getInputStream(jarEntry))

                    }

                }

                val dest = outputProvider.getContentLocation(
                    it.file.name,
                    it.contentTypes,
                    it.scopes,
                    Format.JAR
                )
                FileUtils.copyFile(it.file, dest)
            }

            transformInvocation.inputs.forEach {


                it.directoryInputs.forEach {
                    it.file.listFiles()?.forEach {
                        replaceRInfo(it)
                    }
                }


                it.jarInputs.forEach {
                    replaceRInfo(it)
                }

            }

        }


    }


    private fun replaceRInfo(it: JarInput) {


        val jarFile = JarFile(it.file)

        val entries = jarFile.entries()
        while (entries.hasMoreElements()) {
            val jarEntry = entries.nextElement()
            if (jarEntry.name.endsWith(".class")) {
                val bytes = IOUtils.toByteArray(jarFile.getInputStream(jarEntry))

                replaceRInfo(bytes)
            }
        }
    }

    private fun replaceRInfo(bytes: ByteArray?) {
        bytes?.apply {
            val classReader = ClassReader(this)
            val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
            classReader.accept(object : ClassVisitor(Opcodes.ASM5) {

                override fun visitField(
                    access: Int,
                    name: String?,
                    descriptor: String?,
                    signature: String?,
                    value: Any?
                ): FieldVisitor {
                    return super.visitField(access, name, descriptor, signature, value)
                }

                override fun visitMethod(
                    access: Int,
                    name: String?,
                    descriptor: String?,
                    signature: String?,
                    exceptions: Array<out String>?
                ): MethodVisitor {

                    var mv = super.visitMethod(access, name, descriptor, signature, exceptions)

                    mv = object : MethodVisitor(Opcodes.ASM5, mv) {

                        override fun visitFieldInsn(
                            opcode: Int,
                            owner: String?,
                            name: String?,
                            descriptor: String?
                        ) {
                            return super.visitFieldInsn(opcode, owner, name, descriptor)
                        }
                    }
                    return mv
                }

            }, ClassReader.EXPAND_FRAMES)
        }
    }


    /**
     * 替换文件中的 R 信息
     */
    private fun replaceRInfo(it: File) {


    }

    private fun collectRField(inputStream: InputStream?) {
        inputStream?.apply {

            val bytes = IOUtils.toByteArray(inputStream)

            val classReader = ClassReader(bytes)
            val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)

            val classVisitor = ThinRClassVisitor(Opcodes.ASM5, classWriter)
            classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
        }

    }


    val rPattern = Regex(".*/R\\$.*\\.class|.*/R\\.class")

    //com/example/androidlib/R2$anim.class
    val r2Pattern = Regex(".*/R2?\\$.*\\.class|.*/R2?\\.class")

    fun isRClass(path: String): Boolean {

        return rPattern.matches(path) or r2Pattern.matches(path)
    }

    inner class ThinRClassVisitor(api: Int, classVisitor: ClassVisitor?) :
        ClassVisitor(api, classVisitor) {


        override fun visitField(
            access: Int,
            name: String?,
            descriptor: String?,
            signature: String?,
            value: Any?
        ): FieldVisitor {


            if (value is Int) {
                //todo 收集 R 变量
//                Rmap[]

            }

            return super.visitField(access, name, descriptor, signature, value)
        }
//
//        override fun visitMethod(
//            access: Int,
//            name: String?,
//            descriptor: String?,
//            signature: String?,
//            exceptions: Array<out String>?
//        ): MethodVisitor {
//            var mv =  super.visitMethod(access, name, descriptor, signature, exceptions)
//
//          return  object : MethodVisitor(Opcodes.ASM5) {
//
//              override fun visitFieldInsn(opcode: Int, owner: String?, name: String?, descriptor: String?) {
//                  super.visitFieldInsn(opcode, owner, name, descriptor)
//
//              }
//            }
//        }
    }

}